import { Injectable, OnModuleInit } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { OperationLoggerService } from '../../services/operation-logger.service';

@Injectable()
export class RedisService implements OnModuleInit {
  private isConnected: boolean = false;
  private readonly maxRetries = 3;
  private readonly retryDelay = 1000; // 1秒

  constructor(
    @InjectRedis() private readonly redis: Redis,
    private readonly logger: OperationLoggerService
  ) {}

  async onModuleInit() {
    await this.initializeRedisConnection();
  }

  private async initializeRedisConnection() {
    try {
      await this.redis.ping();
      this.isConnected = true;
      this.logger.logSystemOperation('Redis connection test successful');
    } catch (error) {
      this.isConnected = false;
      this.logger.logError('Redis initialization', error);
    }

    this.setupEventListeners();
  }

  private setupEventListeners() {
    this.redis.on('error', (err) => {
      this.isConnected = false;
      this.logger.logError('Redis connection', err);
    });

    this.redis.on('connect', () => {
      this.isConnected = true;
      this.logger.logSystemOperation('Redis connected');
    });

    this.redis.on('ready', () => {
      this.isConnected = true;
      this.logger.logSystemOperation('Redis ready');
    });

    this.redis.on('reconnecting', () => {
      this.logger.logSystemOperation('Redis reconnecting');
    });

    this.redis.on('end', () => {
      this.isConnected = false;
      this.logger.logSystemOperation('Redis connection ended');
    });

    // 添加更多事件监听
    this.redis.on('close', () => {
      this.isConnected = false;
      this.logger.logSystemOperation('Redis connection closed');
    });

    this.redis.on('reconnected', () => {
      this.isConnected = true;
      this.logger.logSystemOperation('Redis reconnected successfully');
    });
  }

  private async retryOperation<T>(operation: () => Promise<T>, key: string): Promise<T> {
    let lastError;
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        if (!this.isConnected) {
          await this.reconnect();
        }
        return await operation();
      } catch (error) {
        lastError = error;
        this.logger.logError('Redis operation failed', {
          error,
          attempt,
          key,
          maxRetries: this.maxRetries
        });
        
        if (error.message.includes('ECONNRESET')) {
          await this.reconnect();
        }
        
        if (attempt < this.maxRetries) {
          await new Promise(resolve => setTimeout(resolve, this.retryDelay * attempt));
        }
      }
    }
    throw lastError;
  }

  private async reconnect(): Promise<void> {
    try {
      await this.redis.disconnect();
      await this.redis.connect();
      await this.redis.ping();
      this.isConnected = true;
      this.logger.logSystemOperation('Redis reconnected successfully');
    } catch (error) {
      this.isConnected = false;
      this.logger.logError('Redis reconnection failed', error);
      throw error;
    }
  }

  async get(key: string): Promise<string | null> {
    return this.retryOperation(
      async () => {
        const result = await this.redis.get(key);
        this.logger.logRedisOperation('GET', key);
        return result;
      },
      key
    );
  }

  async set(key: string, value: string, ttl?: number): Promise<void> {
    return this.retryOperation(
      async () => {
        if (ttl) {
          await this.redis.set(key, value, 'EX', ttl);
        } else {
          await this.redis.set(key, value);
        }
        this.logger.logRedisOperation('SET', key);
      },
      key
    );
  }

  async del(key: string): Promise<void> {
    return this.retryOperation(
      async () => {
        await this.redis.del(key);
        this.logger.logRedisOperation('DEL', key);
      },
      key
    );
  }

  async exists(key: string): Promise<boolean> {
    return this.retryOperation(
      async () => {
        const result = await this.redis.exists(key);
        this.logger.logRedisOperation('EXISTS', key);
        return result === 1;
      },
      key
    );
  }

  async healthCheck(): Promise<boolean> {
    try {
      await this.redis.ping();
      return true;
    } catch (error) {
      this.logger.logError('Redis health check failed', error);
      return false;
    }
  }
}


